package com.ztom.agent;

import com.google.common.base.Splitter;
import com.ztom.agent.annotation.InjectBeginMethod;
import com.ztom.agent.annotation.InjectClass;
import com.ztom.agent.annotation.InjectEndMethod;
import com.ztom.agent.asm.plugin.http.servletrequest.HttpServletRequestPlugin;
import com.ztom.agent.asm.plugin.jedis.JedisCmdPlugin;
import com.ztom.agent.asm.plugin.PluginInfoHolder;
import com.ztom.agent.bean.InjectClassInfo;
import com.ztom.agent.config.TraceFilter;
import com.ztom.agent.trace.DapperSpanLifecycleListener;
import com.ztom.agent.trace.Tracer;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ZhangTao
 */
public class TraceBootstrap extends AbstractBootstrap {

    private TraceBootstrap(){
        // do nothing
    }

    public static TraceBootstrap getInstance() {
        return Holder.TRACE_BOOTSTRAP;
    }

    private static class Holder {
        private static final TraceBootstrap TRACE_BOOTSTRAP = new TraceBootstrap();
    }

    @Override
    public boolean initOther() {
        // 初始化插件
        initPlugins();
        // 初始化span生命周期
        Tracer.getInstance().addSpanLifecycleListener(new DapperSpanLifecycleListener());
        return true;
    }

    @SuppressWarnings("unchecked")
    private void initPlugins() {
        Class[] clzCommon = new Class[] {
                HttpServletRequestPlugin.class,
                JedisCmdPlugin.class
        };

        List<Class> clazzList = new ArrayList<>();
        for (Class clazz : clzCommon) {
            if (TraceFilter.isNeedLoad(clazz.getName())) {
                clazzList.add(clazz);
            }
        }

        for (Class cls : clazzList) {
            InjectClass anno = (InjectClass) cls.getAnnotation(InjectClass.class);
            if (anno == null) {
                continue;
            }

            String className = anno.injectClass();
            PluginInfoHolder.getClassSet().add(className);
            String[] methodList = anno.injectMethod();
            List<InjectClassInfo> list = new ArrayList<>();

            for (String item : methodList) {
                InjectClassInfo classInfo = new InjectClassInfo();
                classInfo.setClazz(cls);
                List<String> parts = Splitter.on(" ").splitToList(item);
                if (parts.size() > 2) {
                    continue;
                }

                if (parts.size() == 2) {
                    // HttpServletRequestPlugin
                    classInfo.setMethodName(parts.get(0));
                    classInfo.setMethodDesc(parts.get(1));
                }
                if (parts.size() == 1) {
                    // JedisCmdPlugin
                    classInfo.setMethodName(parts.get(0));
                    classInfo.setMethodDesc("*");
                }
                list.add(classInfo);
            }
            PluginInfoHolder.getPluginMap().put(className, list);

            Method[] methods = cls.getDeclaredMethods();
            Method injectBeginMethod = null;
            Method injectEndMethod = null;
            for (Method method : methods) {
                InjectBeginMethod onBeginAnnotation = method.getAnnotation(InjectBeginMethod.class);
                if (onBeginAnnotation != null) {
                    injectBeginMethod  = method;
                    // 讲道理, 每个类应该只有一个方法有此注解
                    continue;
                }

                InjectEndMethod onEndAnnotation = method.getAnnotation(InjectEndMethod.class);
                if (onEndAnnotation != null) {
                    injectEndMethod = method;
                }
            }
            for (InjectClassInfo classInfo : list) {
                classInfo.setInjectBeginMethod(injectBeginMethod);
                classInfo.setInjectEndMethod(injectEndMethod);
            }
        }
    }
}
